﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using PropertyGridEx;
using System.Globalization;
using System.Drawing.Text;


namespace MercuryFE
{
    public partial class ConfigurationEditor : Form
    {

        ConfigFile _config = null;

        SectionHeaderType _sectionHeaderType= SectionHeaderType.SingleLineSquareBracket;
        ValueFormatType _valueFormatType = ValueFormatType.EqualSign;
        string _configFileLocation;
        string _configDescFileLocation;

        public enum SectionHeaderType
        {
            ThreeLineHash, SingleLineSquareBracket
        }

        public enum ValueFormatType
        {
            Delimited, EqualSign
        }


        public ConfigurationEditor(string configFile, string configDescFile) //, SectionHeaderType sectionHeaderType, ValueFormatType valueFormatType)
        {
            InitializeComponent();
            _configFileLocation = configFile;
            _configDescFileLocation = configDescFile;
            //_sectionHeaderType = sectionHeaderType;
            //_valueFormatType = valueFormatType;

            _config = loadConfig();
            populateProperties();


        }

        void tsbSave_Click(object sender, System.EventArgs e)
        {
            saveConfig(this.pgeMain);
        }

        private void saveConfig(PropertyGridEx.PropertyGridEx configuration)
        {


            // load all the values back into the config object
            foreach (CustomProperty item in configuration.Item)
            {
                string value = "";
                switch(_config[item.Category][item.Name].Type) {
                    case "font":
                        value = "\"" + item.Value.ToString() + "\"";
                        break;
                    case "boolNum":
                        value = ((bool)item.Value) ? "1" : "0";
                        break;
                    case "decimal":
                        value = String.Format("{0:0.0}", (double)item.Value);
                        break;
                    default:
                        value = item.Value.ToString();
                        break;
                }
                _config[item.Category][item.Name].Value = value;
            }

            // backup original
            try
            {
                File.Copy(_configFileLocation, _configFileLocation + ".BAK_" + DateTime.Now.ToString("yyyyMMdd_HHmmss"));
            }
            catch (Exception e)
            {
                MessageBox.Show("Error saving the Configuration  file. [" + _configFileLocation + "]"
                        + Environment.NewLine + e.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }


            // write file
            int delimitSize = 0;
            if (_valueFormatType == ValueFormatType.Delimited)
            {
                // get the largest name size
                delimitSize = (from x in _config.Sections
                            from y in x.Items
                            select y.Name.Length).Max();

                delimitSize += 5; // add on some buffer
            }


            using (TextWriter tw = new StreamWriter(_configFileLocation, false))
            {

                foreach (ConfigSection section in _config.Sections)
                {
                    // write out section header
                    if (_sectionHeaderType == SectionHeaderType.SingleLineSquareBracket)
                        tw.WriteLine("[" + section.Section + "]");
                    else if (_sectionHeaderType == SectionHeaderType.ThreeLineHash)
                    {
                        tw.WriteLine("#");
                        tw.WriteLine("# " + section.Section);
                        tw.WriteLine("#");
                    }

                    // now write out items
                    foreach (ConfigItem item in section.Items)
                    {

                        if (_valueFormatType == ValueFormatType.Delimited)
                        {
                            tw.WriteLine(item.Name.PadRight(delimitSize, ' ') + item.Value);
                        }
                        else if (_valueFormatType == ValueFormatType.EqualSign)
                        {
                            tw.WriteLine(item.Name + "=" + item.Value);
                        }
                    }

                    tw.WriteLine();
                }
            }
            MessageBox.Show(_configFileLocation + " saved");
        }

        private void populateProperties()
        {
            CultureInfo cultureInfo = CultureInfo.InvariantCulture;
            TextInfo textInfo = cultureInfo.TextInfo;


            pgeMain.Item.Clear();
            foreach (ConfigSection section in _config.Sections)
            {
                string category = textInfo.ToTitleCase(section.Section.ToLowerInvariant());
                string categoryHelp = section.SectionHelp;

                foreach (ConfigItem item in section.Items)
                {
                    string itemHelp = "";
                    if (categoryHelp != null)
                        itemHelp = categoryHelp;
                    else
                        itemHelp = item.Description;

                    object value = null;
                    switch (item.Type)
                    {
                        case "boolNum" :
                            value = (bool)(item.Value == "1" ? true : false);
                            pgeMain.Item.Add(item.Name, value, false, category, itemHelp, true);
                            break;
                        case "number":
                            value = int.Parse(item.Value);
                            pgeMain.Item.Add(item.Name, value, false, category, itemHelp, true);

                            //assume settings passed in is low/high number to dump into choices
                            if (item.TypeSettings != null)
                            {
                                string[] aTemp = item.TypeSettings.Split(',');
                                List<int> numTemp = new List<int>();
                                for (int x = int.Parse(aTemp[0]); x <= int.Parse(aTemp[1]); x++)
                                    numTemp.Add(x);
                                pgeMain.Item[pgeMain.Item.Count - 1].Choices = new CustomChoices(numTemp.ToArray(), false);
                            }

                            break;
                        case "pathMulti":
                            value = item.Value ?? "";
                            pgeMain.Item.Add(item.Name, value, false, category, itemHelp, true);
                            pgeMain.Item[pgeMain.Item.Count - 1].OnClick += MultiPath_OnClick;
                            break;
                            
                        case "pathSingle":
                            value = (string)item.Value ?? "";
                            pgeMain.Item.Add(item.Name, value, false, category, itemHelp, true);
                            pgeMain.Item[pgeMain.Item.Count - 1].UseFolderEditor = true;
                            break;


                        case "fileSingle":
                            value = (string)item.Value ?? "";
                            pgeMain.Item.Add(item.Name, value, false, category, itemHelp, true);
                            pgeMain.Item[pgeMain.Item.Count - 1].UseFileNameEditor = true;
                            pgeMain.Item[pgeMain.Item.Count - 1].FileNameDialogType = UIFilenameEditor.FileDialogType.SaveFileDialog;

                            string filter = "All files (*.*)|*.*|Text files (*.txt)|*.txt";
                            if(item.TypeSettings != null)
                                filter = item.TypeSettings;
                            pgeMain.Item[pgeMain.Item.Count - 1].FileNameFilter = filter;
                            break;
                        case "fileMulti":
                            value = item.Value ?? "";
                            pgeMain.Item.Add(item.Name, value, false, category, itemHelp, true);
                            pgeMain.Item[pgeMain.Item.Count - 1].OnClick += MultiFile_OnClick;
                            break;

                        case "multipleChoice":
                            value = item.Value ?? "";
                            pgeMain.Item.Add(item.Name, value, false, category, itemHelp, true);
                            pgeMain.Item[pgeMain.Item.Count - 1].Choices = new CustomChoices(item.TypeSettings.Split(',').ToArray(), false);
                            break;


                        case "font":
                            List<string> fontNames = new List<string>();
                            InstalledFontCollection fonts = new InstalledFontCollection();
                            for (int i = 0; i < fonts.Families.Length; i++)
                                fontNames.Add(fonts.Families[i].Name);
                            fontNames.Sort();

                            value = item.Value.TrimStart('"').TrimEnd('"');
                            pgeMain.Item.Add(item.Name, value, false, category, itemHelp, true);
                            pgeMain.Item[pgeMain.Item.Count - 1].Choices = new CustomChoices(fontNames.ToArray(), false);

                            break;


                        case "decimal":


                            value = double.Parse(item.Value);
                            pgeMain.Item.Add(item.Name, value, false, category, itemHelp, true);

                            if (item.TypeSettings != null)
                            {
                                string[] aTemp = item.TypeSettings.Split(',');
                                List<double> numTemp = new List<double>();
                                double start = double.Parse(aTemp[0]);
                                double end = double.Parse(aTemp[1]);
                                double increment = 0.1;

                                for (double x = start; x <= end; )
                                {
                                    numTemp.Add(Math.Round(x, 1));
                                    x = Math.Round(x + increment,1);
                                }
                                pgeMain.Item[pgeMain.Item.Count - 1].Choices = new CustomChoices(numTemp.ToArray(), false);
                            }

                            break;

                        default:
                            value = (string)item.Value ?? "";
                            pgeMain.Item.Add(item.Name, value, false, category, itemHelp, true);
                            break;
                    }
                    
                    
                }
            }
            pgeMain.Refresh();
            
        }

        private object MultiFile_OnClick(object sender, EventArgs e)
        {
            CustomProperty prop = (CustomProperty)((CustomProperty.CustomPropertyDescriptor)sender).CustomProperty;
            string value = prop.Value.ToString();

            MultiItemEditor frmMPE = new MultiItemEditor(value, MultiItemEditor.MultiItemType.File, ';');
            frmMPE.ShowDialog();
            value = frmMPE.ItemString;
            frmMPE.Dispose();
            return value;
        }

        private object MultiPath_OnClick(object sender, EventArgs e)
        {
            CustomProperty prop = (CustomProperty)((CustomProperty.CustomPropertyDescriptor)sender).CustomProperty;
            string value = prop.Value.ToString();

            MultiItemEditor frmMPE = new MultiItemEditor(value, MultiItemEditor.MultiItemType.Path, ';');
            frmMPE.ShowDialog();
            value = frmMPE.ItemString;
            frmMPE.Dispose();
            return value;
        }

        private ConfigFile loadConfig()
        {

            // defaults for standard ini file
            _sectionHeaderType = SectionHeaderType.SingleLineSquareBracket;
            _valueFormatType = ValueFormatType.EqualSign;

            ConfigFile configDesc = new ConfigFile();
	        // load the description file
            if (!string.IsNullOrEmpty(_configDescFileLocation) && File.Exists(_configDescFileLocation))
            {
                try
                {
                    var configDescDoc = XElement.Load(_configDescFileLocation);


                    string sectionHeaderType = (string)configDescDoc.Attribute("sectionHeaderType");
                    string valueFormatType = (string)configDescDoc.Attribute("valueFormatType");

                    _sectionHeaderType = (SectionHeaderType)Enum.Parse(typeof(SectionHeaderType), sectionHeaderType);
                    _valueFormatType = (ValueFormatType)Enum.Parse(typeof(ValueFormatType), valueFormatType);


                    List<ConfigSection> configDescSections = (from x in configDescDoc.Descendants("section")
                                                              select new ConfigSection
                                                              {
                                                                  Section = x.Attribute("name").Value
                                                                  ,
                                                                  SectionHelp = (string)x.Attribute("sectionHelp")
                                                                  ,
                                                                  Items =
                                                                      (
                                                                    (from y in x.Descendants("item")
                                                                     select new ConfigItem
                                                                     {
                                                                         Name = y.Attribute("name").Value
                                                                         ,
                                                                         Type = y.Attribute("type").Value
                                                                         ,
                                                                         Description = y.Value
                                                                         ,
                                                                         TypeSettings = (string)y.Attribute("typeSetting")
                                                                         //Value
                                                                     }).ToList()
                                                                      )

                                                              }).ToList();

                    //configDesc = new ConfigFile();
                    configDesc.Sections = configDescSections;
                }
                catch (Exception e)
                {
                    MessageBox.Show("Error loading the Configuration description file. [" + _configDescFileLocation + "]"
                        + Environment.NewLine + e.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    this.Close();
                }
            } 





	        // actual Config File
	        ConfigFile config = new ConfigFile();
	        string[] buffer = File.ReadAllLines(_configFileLocation);
        	
            // ************ PROCESS THE INI FILE ********************
        	
	        // regular expression setup to replace multiple spaces with a value
	        RegexOptions options = RegexOptions.None;
	        Regex regex = new Regex(@"[ ]{2,}", options);

            char[] commentLineStart = new char[] {'<','#',';'};
	        int headerCount = 0;
	        ConfigSection Section = null;
	        List<ConfigItem> currentItems = null;
            string sectionHeader = "";

            try
            {
                foreach (string line in buffer)
                {
                    bool newSection = false;

                    if (_sectionHeaderType == SectionHeaderType.ThreeLineHash && line.StartsWith("#"))
                    {
                        headerCount++;
                        // process header..
                        if (headerCount == 1)
                            continue;
                        else if (headerCount == 2)
                        {
                            sectionHeader = line.Substring(2);
                            newSection = true;
                        }
                        else if (headerCount == 3)
                        {
                            headerCount = 0;
                            continue;
                        }
                    }
                    else if (_sectionHeaderType == SectionHeaderType.SingleLineSquareBracket && line.StartsWith("["))
                    {
                        sectionHeader = line.Trim().TrimStart('[').TrimEnd(']');
                        newSection = true;
                    }

                    // lines to ignore
                    else if (line == "" || commentLineStart.Contains(char.Parse(line.TrimStart().Substring(0, 1))))
                        //if (line.TrimStart().StartsWith("<"))
                        continue;

                    if (newSection)
                    {
                        // store previous section...
                        if (Section != null && config != null)
                        {
                            Section.Items = currentItems;
                            config.Sections.Add(Section);
                        }

                        // set up new empty section
                        Section = new ConfigSection();
                        Section.Section = sectionHeader;
                        currentItems = new List<ConfigItem>();
                        continue;
                    }


                    // reset header count, in case have used a comment line
                    headerCount = 0;

                    //if(status==ReadStatus.Item && line != "") {
                    if (line != "")
                    {
                        string[] temp = null;
                        if (_valueFormatType == ValueFormatType.Delimited)
                        {
                            string tempo = regex.Replace(line, @",");
                            temp = tempo.Split(',');
                        }
                        else if (_valueFormatType == ValueFormatType.EqualSign)
                        {
                            temp = line.Split('=');
                        }

                        ConfigItem item = new ConfigItem()
                        {
                            Name = temp[0].Trim(),
                            Value = temp[1].Trim(),
                            Description = sectionHeader + " --> " + temp[0].Trim()
                        };
                        currentItems.Add(item);

                    }

                }

                // close off..
                //if(status == ReadStatus.Item && Section != null && config != null) {
                if (Section != null && config != null)
                {
                    Section.Items = currentItems;
                    config.Sections.Add(Section);
                }
            }
            catch (Exception e)
            {
                MessageBox.Show("Error loading the Configuration file. [" + _configFileLocation + "]"
                    +  Environment.NewLine + e.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.Close();
            }

            // ************END OF PROCESS INI FILE
        	
	        ConfigFile compoundConfig = new ConfigFile();
        	
	        // go from desc -> actual
	        foreach(ConfigSection sectionA in configDesc.Sections) {
		        // add to compound
		        ConfigSection newSection = new ConfigSection();
		        newSection.Section = sectionA.Section;
                newSection.SectionHelp = sectionA.SectionHelp;
        	
		        // find in other
		        //bool sectionFound = false;
		        foreach(ConfigSection sectionB in config.Sections) {
			        if(sectionA.Section==sectionB.Section) {
				        //sectionFound = true;
        				
				        // now go through items
				        foreach(ConfigItem itemA in sectionA.Items) {
					        ConfigItem newItem = itemA;
        				
					        //bool itemFound = false;
					        foreach(ConfigItem itemB in sectionB.Items) {
						        if(itemA.Name==itemB.Name) {
							       // itemFound = true;
        							
							        newItem.Value = itemB.Value;
							        break;
        							
						        }
					        }
					        newSection.Items.Add(newItem);
				        }
				        break;
			        }
		        }
		        compoundConfig.Sections.Add(newSection);
        	
	        }
        	
		        // now from  actual -> desc (ie, add missing items)
	        foreach(ConfigSection sectionA in config.Sections) {
        //		// add to compound
        //		ConfigSection newSection = new ConfigSection();
        //		newSection.Section = sectionA.Section;
        	
        	
		        // find in other
		        bool sectionFound = false;
		        foreach(ConfigSection sectionB in compoundConfig.Sections) {
			        if(sectionA.Section==sectionB.Section) {
				        sectionFound = true;
        				
				        // now go through items
				        foreach(ConfigItem itemA in sectionA.Items) {
					        bool itemFound = false;
					        foreach(ConfigItem itemB in sectionB.Items) {
						        if(itemA.Name==itemB.Name) {
							        itemFound = true;
							        break;
        							
						        }
					        }
					        if(!itemFound) {
						        sectionB.Items.Add(itemA);	
					        }
				        }
				        break;

			        }
		        }
		        if(!sectionFound) {
			        compoundConfig.Sections.Add(sectionA);
		        }
        	
	        }
        	

            return compoundConfig;
        }

        public class MultiFolder
        {
            public Dictionary<string, CustomProperty> folders;

            public MultiFolder()
            {
                folders = new Dictionary<string, CustomProperty>();
            }

            public MultiFolder(string folderString)
            {

                folders = new Dictionary<string, CustomProperty>();
                List<string> temp = folderString.Split(';').ToList();
                foreach (string folder in temp)
                {
                    CustomProperty prop = new CustomProperty("", folder, false, "", "MultiFolder", true);
                    prop.UseFolderEditor = true;
                    folders.Add(folder, prop);
                }

            }

            public override string ToString()
            {
                StringBuilder sb = new StringBuilder();
                foreach (KeyValuePair<string, CustomProperty> item in folders)
                {
                    if (sb.ToString() != "")
                        sb.Append(";");
                    sb.Append(item.Key);
                }
                return (sb.ToString());
            }
        }

        public class ConfigFile
        {
            public IList<ConfigSection> Sections { get; set; }

            public ConfigFile()
            {
                Sections = new List<ConfigSection>();
            }

            public ConfigSection this[string sectionName]
            {
                get
                {
                    ConfigSection sectionSearch = null;
                    foreach (ConfigSection currentSection in this.Sections)
                    {
                        if (currentSection.Section.ToUpper() == sectionName.ToUpper())
                        {
                            sectionSearch = currentSection;
                            break;

                        }
                    }
                    return sectionSearch;
                }
            }

        }

        public class ConfigSection
        {
            public string Section { get; set; }
            public string SectionHelp { get; set; }
            public IList<ConfigItem> Items { get; set; }


            public ConfigSection()
            {
                Items = new List<ConfigItem>();
            }

            public ConfigItem this[string itemName]
            {
                get
                {
                    ConfigItem itemSearch = null;
                    foreach (ConfigItem currentItem in this.Items)
                    {
                        if (currentItem.Name.ToUpper() == itemName.ToUpper())
                        {
                            itemSearch = currentItem;
                            break;

                        }
                    }
                    return itemSearch;
                }
            }
        }

        public class ConfigItem
        {
            public string Name { get; set; }
            public string Type { get; set; }
            public string Description { get; set; }
            public string TypeSettings { get; set; }
            public string Value { get; set; }
        }

    }


   
}
